home *** CD-ROM | disk | FTP | other *** search
- /**************************************************************************
- * obextract.c: The main program for ObExtract.
- * Extract patch data from a 100-patch dump file and either
- * store it in individual files, or send it via MIDI to the
- * Oberheim synth.
- * A part of OberSuite for the Commodore Amiga.
- *
- * Author: Daniel Barrett, barrett@cs.umass.edu.
- * Version: 1.0.
- * Copyright: None! This program is in the Public Domain.
- * Please share it with others.
- ***************************************************************************/
-
- #include "decl.h"
- #include "obextract.h"
-
- char *version = "$VER: ObExtract " VERSION " " VERDATE;
-
-
- /***************************************************************************
- * The main program.
- ***************************************************************************/
-
- main(argc, argv)
- int argc; char *argv[];
- {
- int fileArg; /* Argument position of the filename. */
-
- /* We use a bit vector to "check off" the numbers of the patches
- * that we will be extracting. */
-
- BITS numbers[BITFIELD_LENGTH];
-
- Enable_Abort = 0; /* Disable ^C aborts. */
- strcpy(programName, BaseName(argv[0])); /* Global variable. */
- thePrintStyle = VERBOSE; /* Global variable. */
- theDestination = DEST_NONE; /* Global variable. */
- overwriteAll = FALSE; /* Global variable. */
- useTheRealNames = FALSE; /* Global variable. */
-
- if (argc == 1)
- {
- ShortUsageMsg();
- BegForUsage();
- }
- else if ((argc == 2) && (!strcmp(argv[1], "?")))
- DetailedUsage();
- else if (!HandleOptions(argc, argv))
- BegForUsage();
- else if (optind >= argc-1)
- ErrorMsg(ERROR_NUMARGS);
- else if (! (fileArg = ReadRanges(numbers, optind, argv)) )
- ErrorMsg(ERROR_PATCHNUM);
- else if (fileArg != argc-1)
- ErrorMsg(ERROR_NUMARGS);
- else
- ObExtract(numbers, argv[fileArg]);
- }
-
-
- /*
- * Read patch data from the given file. Extract the data for the patches
- * specified in the bit vector "numbers".
- */
-
- void ObExtract(BITS numbers[], char *filename)
- {
- PATCHINFO pi;
-
- InitPatchInfo(&pi);
- pi.source = PI_SOURCE_FILE;
-
- if (!LookAtFileSize(&pi, filename))
- ;
- else if (!AllocPatchInfo(&pi))
- ErrorMsg(ERROR_MALLOC);
- else if (!GetPatchFromFile(&pi, filename))
- ErrorMsg(ERROR_GETFAILED);
- else if (!FigureThingsOutFromHeader(&pi))
- ;
- else if (!DoTheExtract(&pi, numbers, filename))
- ;
- else
- ; /* Do nothing. */
-
- FreePatchInfo(&pi);
- }
-
-
- /*
- * Given a PATCHINFO struct full of patch data, and a bit vector indicating
- * which patches to use, extract the patch data. If we are extracting to
- * a file, the string "filename" helps us form unique file names for the
- * extracted patches.
- * Return TRUE on success (else FALSE).
- */
-
- BOOL DoTheExtract(PATCHINFO *pi, BITS numbers[], char *filename)
- {
- int i;
- long offset = 0L;
- BOOL success = TRUE;
- long tooFar;
- UBYTE low=FIRST_PATCH, high=LAST_PATCH;
-
- /* We need at least "tooFar" bytes of data to be present. */
-
- tooFar = pi->numPatches * pi->rightSize;
-
- /* Figure out the lowest-numbered and highest-numbered patch we are
- * are extracting. */
-
- ComputeLowAndHigh(numbers, &low, &high);
-
- /* If we are extracting to MIDI, turn on the serial stuff. */
-
- if ((theDestination & DEST_SYNTH) && !SerialSetup())
- return(FALSE);
-
- /* For each patch that we want to extract, compute its offset in the
- * patch data, extract the data, and send it on its way. */
-
- for (i=low; (i<=high) && success; i++)
- {
- if (CtrlcCheck())
- {
- ErrorMsg(ERROR_CTRLC);
- success = FALSE;
- break;
- }
-
- offset = i * pi->rightSize;
- if (offset >= tooFar)
- {
- ErrorMsg(ERROR_DATATOOSMALL);
- break;
- }
- else if (BitOn(numbers, i) && VerifyPatch(pi, offset))
- {
- if (theDestination & DEST_SYNTH)
- success &= ExtractOneToSynth(pi, offset);
- if (theDestination & DEST_FILE)
- success &= ExtractOneToFile(pi, offset,
- filename);
- }
- }
-
- if (theDestination & DEST_SYNTH)
- SerialShutdown();
-
- return(success && (offset < tooFar));
- }
-
-
- /*
- * Look at the bit vector "numbers" and find the lowest and highest
- * numbered bits that are turned on. Store their values in "low"
- * and "high", respectively.
- *
- * We are GUARANTEED that before entering this function, "numbers" has
- * at least 1 bit turned on between its FIRST_PATCH'th and LAST_PATCH'th
- * bits (inclusive)... thanks to ReadRanges().
- */
-
- void ComputeLowAndHigh(BITS numbers[], UBYTE *low, UBYTE *high)
- {
- UBYTE i;
-
- /* Find the lowest numbered bit by scanning the bit vector from lowest
- * to highest bits until we find a bit that is turned on.
- * We initialize low to LAST_PATCH so we have a valid answer no matter
- * what. */
-
- *low = LAST_PATCH;
- for (i=FIRST_PATCH; i <= LAST_PATCH; i++)
- {
- if (BitOn(numbers, i))
- {
- (*low) = i;
- break;
- }
- }
-
- /*
- * Find the highest patch number by scanning the bit vector from
- * the highest bit down to "low" (calculated above), looking for a
- * bit that is turned on.
- */
-
- (*high) = (*low);
- for (i=LAST_PATCH; i > (*low); i--)
- {
- if (BitOn(numbers, i))
- {
- (*high) = i;
- break;
- }
- }
- }
-
- /***************************************************************************
- * Extracting patches to FILES.
- ***************************************************************************/
-
- BOOL ExtractOneToFile(PATCHINFO *pi, long offset, char *infile)
- {
- PATCHINFO localPI;
- char outfile[BUFSIZ];
- char *baseName = NULL;
- BOOL success = TRUE;
-
- /* Extract the base name of the input file, minus leading directories. */
-
- if ((baseName = BaseName(infile)) == NULL)
- return(FALSE);
-
- /* Create the name of the output file. */
-
- MakePatchFileName(outfile, baseName, pi, offset);
- if (OUTPUT_ALLOWED)
- PrintPatchInfo(pi, offset);
-
- /* Make sure that the file, if it exists, may safely be overwritten. */
-
- if (!overwriteAll && DontOverwriteExistingFile(outfile))
- ;
-
- else
- {
- if (CtrlcCheck())
- {
- ErrorMsg(ERROR_CTRLC);
- return(FALSE);
- }
-
- localPI = (*pi);
- localPI.data = pi->data + offset;
- localPI.numPatches = 1;
-
- if (success = PutPatchToFile(&localPI, outfile))
- {
- if (OUTPUT_ALLOWED)
- printf(" --> %s\n", outfile);
- }
- if (OUTPUT_ALLOWED)
- putchar('\n');
- }
-
- if (CtrlcCheck())
- {
- ErrorMsg(ERROR_CTRLC);
- return(FALSE);
- }
- else
- return(success);
- }
-
-
- /*
- * Construct the output file name "outfile". Its format is
- *
- * orig + '.' + <'S' or 'M'> + <2-digit patch number>
- *
- * unless useTheRealNames is true. In that case, we form the name as
- *
- * <real patch name> + '.' + <'S' or 'M'> + <2-digit patch number>
- */
-
- void MakePatchFileName(char *outfile, char *orig, PATCHINFO *pi, long offset)
- {
- UBYTE patchNum, patchType;
-
- patchType = pi->data[offset + BYTE_PATCHTYPE];
- patchNum = pi->data[offset + BYTE_PATCHNUMBER];
-
- sprintf(outfile, "%s.%c%02d",
- useTheRealNames ? PatchnameToFilename(pi, offset) : orig,
- (patchType == MODE_SINGLE) ? LETTER_SINGLE : LETTER_MULTI,
- patchNum);
- }
-
-
- /*
- * Find the true patch name in pi->data. Convert "troublesome" characters
- * into DEFAULT_FILENAME_CHAR, and strip trailing blanks. Return the
- * resulting string in a static buffer.
- */
-
- char *PatchnameToFilename(PATCHINFO *pi, long offset)
- {
- static char filename[NAME_LENGTH+1];
- int i;
- UBYTE *here = pi->data + offset + pi->nameOffset;
-
- /* Transfer patch name to filename, eliminating non-alphanumeric chars. */
-
- for (i=0; i<NAME_LENGTH; i++)
- {
- if (isalnum(*here) || ((*here) == ' '))
- filename[i] = tolower(*here);
- else
- filename[i] = DEFAULT_FILENAME_CHAR;
-
- here += 2;
- }
-
- filename[NAME_LENGTH] = '\0';
-
- /*
- * Remove trailing blanks, and convert other blanks to
- * DEFAULT_FILENAME_CHAR.
- */
- i = NAME_LENGTH-1;
- while ((i >= 0) && (filename[i] == ' '))
- {
- filename[i--] = '\0';
- }
- while (i >= 0)
- {
- if (filename[i] == ' ')
- filename[i] = DEFAULT_FILENAME_CHAR;
- --i;
- }
-
- /* If filename is now completely empty, use a default name. */
-
- if (filename[0] == '\0')
- strcpy(filename, DEFAULT_PATCH_NAME);
-
- return(filename);
- }
-
-
- /***************************************************************************
- * Extracting patches to MIDI (the synth).
- ***************************************************************************/
-
- BOOL ExtractOneToSynth(PATCHINFO *pi, long offset)
- {
- PATCHINFO localPI;
-
- localPI = (*pi);
- localPI.data = pi->data + offset;
- localPI.numPatches = 1;
-
- if (!FigureThingsOutFromHeader(&localPI))
- return(FALSE);
- else
- return(RepeatedlySend(&localPI));
- }
-
-
- /***************************************************************************
- * Handle the command-line options.
- ***************************************************************************/
-
- BOOL HandleOptions(int argc, char *argv[])
- {
- int c; /* The character to be read. */
- short printed = 0; /* How many "print" options have been chosen? */
- BOOL success = TRUE;
-
- while ((c = getopt(argc, argv, options)) != EOF)
- {
- switch (c)
- {
- case OPT_DEADQUIET:
- thePrintStyle = DEADQUIET;
- printed++;
- break;
- case OPT_SILENT:
- thePrintStyle = SILENT;
- printed++;
- break;
- case OPT_VERBOSE:
- thePrintStyle = VERBOSE;
- printed++;
- break;
- case OPT_SYNTH:
- theDestination |= DEST_SYNTH;
- break;
- case OPT_FILE:
- theDestination |= DEST_FILE;
- break;
- case OPT_OVERWRITE:
- overwriteAll = TRUE;
- break;
- case OPT_USEREALNAMES:
- useTheRealNames = TRUE;
- break;
- default:
- return(FALSE);
- }
- }
-
- if (printed > 1) /* User chose > 1 print option. */
- {
- ErrorMsg(ERROR_TWOPRINTS);
- success = FALSE;
- }
-
- if (!theDestination) /* User didn't choose a dest. */
- {
- ErrorMsg(ERROR_NODEST);
- success = FALSE;
- }
-
- if (useTheRealNames && !(theDestination & DEST_FILE))
- {
- ErrorMsg(ERROR_REALNAMESFILEONLY);
- success = FALSE;
- }
-
- if (overwriteAll && !(theDestination & DEST_FILE))
- {
- ErrorMsg(ERROR_OVERWRITEFILEONLY);
- success = FALSE;
- }
-
- return(success);
- }
-
-
- /*
- * Given a list of ranges beginning at argv[firstArg], calculate their
- * contents and turn on the corresponding bits in the vector "numbers".
- * Return the position of the next unprocessed argument.
- * (Presumably it will be our input filename.)
- */
-
- int ReadRanges(BITS numbers[], int firstArg, char *argv[])
- {
- int upper, lower;
-
- ClearBitfield(numbers);
- while (argv[firstArg] && isdigit(argv[firstArg][0]))
- {
- if (MakeRange(argv[firstArg], &upper, &lower))
- {
- if (!AddToRange(numbers, upper, lower))
- return(0);
- firstArg++;
- }
- else
- return(0);
- }
-
- return(firstArg);
- }
-
-
- /***************************************************************************
- * Usage information.
- ***************************************************************************/
-
- void ShortUsageMsg(void)
- {
- if (!ERR_OUTPUT_ALLOWED) return;
-
- fprintf(stderr,
- "Usage: %s [options] PATCH [PATCH...] filename\n",
- programName);
- }
-
-
- void UsageMsg(void)
- {
- if (!ERR_OUTPUT_ALLOWED) return;
-
- fprintf(stderr,
- "%s extracts individual patches from a %d-patch file.\n",
- programName, NUM_PATCHES);
-
- ShortUsageMsg();
-
- fprintf(stderr, "\nLegal options are:\n");
- fprintf(stderr,
- "\t-%c:\tExtracted patches are put into individual files.\n",
- OPT_FILE);
- fprintf(stderr,
- "\t-%c:\tExtracted patches are sent to the Oberheim.\n",
- OPT_SYNTH);
- fprintf(stderr,
- "\t-%c:\tOverwrite existing files without asking permission."
- "(-%c only)\n",
- OPT_OVERWRITE, OPT_FILE);
- fprintf(stderr,
- "\t-%c:\tUse actual patch names for file names (-%c only).\n",
- OPT_USEREALNAMES, OPT_FILE);
- fprintf(stderr, "\t-%c:\tQuiet output; error messages only.\n",
- OPT_SILENT);
- fprintf(stderr, "\t-%c:\tNo output; not even error messages.\n",
- OPT_DEADQUIET);
- fprintf(stderr, "\t-%c:\tLong output (DEFAULT).\n", OPT_VERBOSE);
- fprintf(stderr, "You MUST specify -%c or -%c (or both).\n",
- OPT_FILE, OPT_SYNTH);
-
- fprintf(stderr, "\nEach \"PATCH\" can be one patch number,");
- fprintf(stderr, " or a range like 3-18 or 64-60.\n");
- fprintf(stderr,
- "Ranges may overlap and be increasing OR decreasing.\n");
-
- fprintf(stderr, "Examples:\n");
- fprintf(stderr, "\t%s -%c 2 5 19-27 HundredPatchFile\n",
- programName, OPT_FILE);
- fprintf(stderr, "\t%s -%c%c 81 85-21 HundredPatchFile\n",
- programName, OPT_SYNTH, OPT_SILENT);
- }
-
-
- char *Version(void)
- {
- static char v[] = VERSION;
- return(v);
- }
-